package struct_config

import (
    "encoding/hex"
    "errors"
    "fmt"
    "reflect"
    "strconv"
    "strings"
    "time"
)

const (
    time_layout = "2006-01-02 15:04:05.000"
)

func StringToValue(vs string, ptrVar interface{}) error {
    val := reflect.ValueOf(ptrVar)
    if val.Kind() != reflect.Ptr {
        return errors.New("value not ptr")
    }
    return StringToReflectValue(vs, val.Elem())
}

func ValueToString(value interface{}) (string, error) {
    val := reflect.ValueOf(value)
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
    }
    return ReflectValueToString(val)
}

func StringToReflectValue(vs string, value reflect.Value) error {
    if len(vs) == 0 {
        return nil
    }

    if !value.CanSet() {
        return errors.New("cannot set")
    }

    ve := value
    switch ve.Interface().(type) {
    case bool:
        vss := strings.ToLower(vs)
        if vss == "0" || vss == "false" {
            ve.SetBool(false)
            break
        }
        if vss == "1" || vss == "true" || vss == "ok" {
            ve.SetBool(true)
            break
        }
        return errors.New("unkown boolean " + vs)
    case string:
        ve.SetString(vs)
    case int, int8, int16, int32, int64:
        vr, err := strconv.ParseInt(vs, 0, 64)
        if err != nil {
            return err
        }
        ve.SetInt(vr)
    case uint, uint8, uint16, uint32, uint64:
        vr, err := strconv.ParseUint(vs, 0, 64)
        if err != nil {
            return err
        }
        ve.SetUint(vr)
    case float32:
        vr, err := strconv.ParseFloat(vs, 32)
        if err != nil {
            return err
        }
        ve.SetFloat(vr)
    case float64:
        vr, err := strconv.ParseFloat(vs, 64)
        if err != nil {
            return err
        }
        ve.SetFloat(vr)
    case []byte:

        buf := make([]byte, hex.DecodedLen(len(vs)))
        hex.Decode(buf, []byte(vs))
        ve.SetBytes(buf)
    case []int:

        vr := StrToIntArray(vs)
        ve.Set(reflect.ValueOf(vr))
    case []uint16:

        vr := StrToU16Array(vs)
        ve.Set(reflect.ValueOf(vr))
    case time.Time:
        vr, err := time.Parse(time_layout, vs)
        if err != nil {
            return err
        }
        ve.Set(reflect.ValueOf(vr))
    case time.Duration:
        str_lower := strings.ToLower(vs)

        if strings.Count(str_lower, "d") == 1 {
            idx := strings.Index(str_lower, "d")
            if idx > 0 {
                if d, err := strconv.ParseInt(str_lower[:idx], 0, 64); err == nil {
                    dur_day := time.Duration(d) * 24 * time.Hour
                    if idx < len(str_lower)-1 {
                        if dur_tm, err := time.ParseDuration(str_lower[idx+1:]); err == nil {
                            ve.Set(reflect.ValueOf(dur_day + dur_tm))
                            break
                        }
                    } else {
                        ve.Set(reflect.ValueOf(dur_day))
                        break
                    }
                }
            }
        }

        if vr, err := time.ParseDuration(vs); err == nil {
            ve.Set(reflect.ValueOf(vr))
            break
        }

        if ns, err := strconv.ParseInt(vs, 0, 64); err == nil {
            var vr = time.Duration(ns)
            ve.Set(reflect.ValueOf(vr))
            break
        }
        return errors.New("invalid time duration format " + vs)
    case *time.Location:

        l, err := time.LoadLocation(vs)
        if err != nil {

            l = time.Local
        }
        ve.Set(reflect.ValueOf(l))
    default:
        return fmt.Errorf("未知类型%s", ve.Kind().String())
    }
    return nil
}

func ReflectValueToString(value reflect.Value) (string, error) {
    var vs string
    switch value.Interface().(type) {
    case bool:
        if value.Bool() {
            vs = "true"
        } else {
            vs = "false"
        }
    case string:
        vs = value.String()
    case int, int8, int16, int32, int64:
        vs = fmt.Sprint(value.Int())
    case uint, uint8, uint16, uint32, uint64:
        vs = fmt.Sprint(value.Uint())
    case float32, float64:
        vs = fmt.Sprint(value.Float())
    case []byte:
        vs = hex.EncodeToString(value.Bytes())
    case []uint16:
        lst, _ := value.Interface().([]uint16)
        vs = U16ArrayToStr(lst)
    case time.Time:
        t, _ := value.Interface().(time.Time)
        vs = t.Format(time_layout)
    case time.Duration:
        d, _ := value.Interface().(time.Duration)
        vs = d.String()
    case *time.Location:
        l, _ := value.Interface().(*time.Location)
        vs = l.String()
    default:
        return vs, fmt.Errorf("未知类型%s", value.Kind().String())
    }

    return vs, nil
}

func SetValueFromString(value interface{}, vs string) error {
    return StringToValue(vs, value)
}

func GetStringFromValue(value interface{}) (string, error) {
    return ValueToString(value)
}
